package service

import (
	"SecKill/common/define"
	"SecKill/common/helper"
	"SecKill/common/models"
	"SecKill/common/response"
	"context"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"strconv"
	"time"
)

// 解决缓存穿透
func GetShopByIdForPassThrough(c *gin.Context) {
	var shopVo models.ShopVo
	id, _ := strconv.Atoi(c.Param("id"))
	if err := helper.QueryWithPassThrough(define.CACHE_SHOP_KEY, id, &shopVo, define.CACHE_SHOP_TTL); err != nil {
		fmt.Println(err)
		response.ResponseFailWithData(c, "店铺不存在！", nil, 0)
		return
	}
	response.ResponseOKWithData(c, "Success", shopVo, 1)
	return

}

// 互斥锁
func GetShopById(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	shopJson, err := models.RDb.Get(context.Background(), define.CACHE_SHOP_KEY+strconv.Itoa(id)).Result()
	if err != nil {
		var shopVo models.ShopVo
		if models.Db.Model(new(models.ShopVo)).Where("id = ?", id).First(&shopVo).Error != nil {
			models.RDb.Set(context.Background(), define.CACHE_SHOP_KEY+strconv.Itoa(id), "", define.CACHE_NULL_TTL)
			response.ResponseFailWithData(c, "店铺不存在！", nil, 0)
			return
		}
		//获取互斥锁
		if tryLock(define.LOCK_SHOP_KEY + strconv.Itoa(id)) {
			defer UnLock(define.LOCK_SHOP_KEY + strconv.Itoa(id)) //释放锁
			models.Db.Model(new(models.ShopVo)).Where("id = ?", id).First(&shopVo)
			shopVoJson, _ := json.Marshal(shopVo)
			models.RDb.Set(context.Background(), define.CACHE_SHOP_KEY+strconv.Itoa(id), shopVoJson, define.CACHE_SHOP_TTL)
		} else {
			time.Sleep(50 * time.Millisecond) //休眠重试
			GetShopById(c)
		}
		response.ResponseOKWithData(c, "Success", shopVo, 1)
		return
	}

	if shopJson == "" {
		models.RDb.Set(context.Background(), define.CACHE_SHOP_KEY+strconv.Itoa(id), "", define.CACHE_NULL_TTL)
		response.ResponseFailWithData(c, "店铺不存在！", nil, 0)
		return

	}

	models.RDb.Expire(context.Background(), define.CACHE_SHOP_KEY+strconv.Itoa(id), define.CACHE_SHOP_TTL)
	var shopVo models.ShopVo
	json.Unmarshal([]byte(shopJson), &shopVo)
	response.ResponseOKWithData(c, "Success", shopVo, 1)
	return
}
func GetShopByIdLogicalExpire(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	cacheKey := define.CACHE_SHOP_KEY + strconv.Itoa(id)
	lockKey := define.LOCK_SHOP_KEY + strconv.Itoa(id)
	var shopVo models.ShopVo
	if err := helper.QueryWithLogicalExpire(cacheKey, lockKey, id, &shopVo, define.CACHE_SHOP_TTL); err != nil {
		response.ResponseFailWithData(c, "店铺不存在！", nil, 0)
		return
	}
	response.ResponseOKWithData(c, "Success", shopVo, 1)
	return
}
func tryLock(key string) bool {
	return models.RDb.SetNX(context.Background(), key, 1, define.LOCK_SHOP_TTL).Val()
}
func UnLock(key string) {
	models.RDb.Del(context.Background(), key)
}
func SaveShop(c *gin.Context) {
	var shop models.Shop
	if err := c.ShouldBindJSON(&shop); err != nil {
		response.ResponseFail(c)
		return
	}
	if err := models.Db.Model(new(models.Shop)).Create(&shop).Error; err != nil {
		response.ResponseFail(c)
		return
	}
	response.ResponseOKWithData(c, "添加成功", shop.Id, 1)
	return
}

func UpdateShop(c *gin.Context) {
	var shop models.Shop
	if err := c.ShouldBindJSON(&shop); err != nil {
		response.ResponseFail(c)
		return
	}
	if shop.Id == 0 {
		response.ResponseFail(c)
		return
	}
	// 开启数据库事务
	tx := models.Db.Begin()
	// 更新数据库
	if err := tx.Model(new(models.Shop)).Where("id = ?", shop.Id).Updates(&shop).Error; err != nil {
		// 发生错误时回滚
		tx.Rollback()
		response.ResponseFail(c)
		return
	}

	// 删除Redis缓存
	if err := models.RDb.Del(context.Background(), define.CACHE_SHOP_KEY+strconv.Itoa(shop.Id)).Err(); err != nil {
		// Redis操作失败时回滚数据库事务
		tx.Rollback()
		response.ResponseFail(c)
		return
	}

	// 提交数据库事务
	if err := tx.Commit().Error; err != nil {
		tx.Rollback()
		response.ResponseFail(c)
		return
	}

	response.ResponseOK(c)
	return
}

func GetShopByType(c *gin.Context) {
	typeId, _ := strconv.Atoi(c.Query("typeId"))
	pageNum, _ := strconv.Atoi(c.DefaultQuery("current", strconv.Itoa(define.DEFAULT_PAGE_NUM)))
	pageSize := define.DEFAULT_PAGE_SIZE
	var shops []models.ShopVo
	var count int64
	db := models.Db.Model(new(models.ShopVo))
	db.Where("type_id = ?", typeId)
	db.Count(&count)
	db.Offset((pageNum - 1) * pageSize).
		Limit(pageSize).
		Find(&shops)
	response.ResponseOKWithData(c, "Success", shops, int(count))

}

func GetShopByName(c *gin.Context) {
	name := c.Query("name")
	pageNum, _ := strconv.Atoi(c.DefaultQuery("current", strconv.Itoa(define.DEFAULT_PAGE_NUM)))
	pageSize := define.MAX_PAGE_SIZE
	var shops []models.ShopVo
	var count int64
	db := models.Db.Model(new(models.ShopVo))
	db.Where("name = ?", name)
	db.Count(&count)
	db.Offset((pageNum - 1) * pageSize).
		Limit(pageSize).
		Find(&shops)
	response.ResponseOKWithData(c, "Success", shops, int(count))

}
